/*
 jQuery autoComplete v1.0.7
 Copyright (c) 2014 Simon Steinberger / Pixabay
 GitHub: https://github.com/Pixabay/jQuery-autoComplete
 License: http://www.opensource.org/licenses/mit-license.php
 */

(function ($) {
    $.fn.autoComplete = function (options) {
        var o = $.extend({}, $.fn.autoComplete.defaults, options);

        // public methods
        if (typeof options == 'string') {
            this.each(function () {
                var that = $(this);
                if (options == 'destroy') {
                    $(window).off('resize.autocomplete', that.updateSC);
                    that.off('blur.autocomplete focus.autocomplete keydown.autocomplete keyup.autocomplete');
                    if (that.data('autocomplete'))
                        that.attr('autocomplete', that.data('autocomplete'));
                    else
                        that.removeAttr('autocomplete');
                    $(that.data('sc')).remove();
                    that.removeData('sc').removeData('autocomplete');
                }
            });
            return this;
        }

        return this.each(function () {
            var that = $(this);
            // sc = 'suggestions container'
            that.sc = that.sc || $('<div class="autocomplete-suggestions ' + o.menuClass + '"></div>');
            that.data('sc', that.sc).data('autocomplete', that.attr('autocomplete'));
            that.attr('autocomplete', 'off');
            that.cache = {};
            that.last_val = '';

            that.updateSC = function (resize, next) {
                that.sc.css({
                    top: that.offset().top + that.outerHeight(),
                    left: that.offset().left,
                    width: that.outerWidth() + 20
                });
                if (!resize) {
                    that.sc.show();
                    if (!that.sc.maxHeight)
                        that.sc.maxHeight = parseInt(that.sc.css('max-height'));
                    if (!that.sc.suggestionHeight)
                        that.sc.suggestionHeight = $('.autocomplete-suggestion', that.sc).first().outerHeight();
                    if (that.sc.suggestionHeight)
                        if (next === undefined)
                            that.sc.scrollTop(0);
                        else {
                            var nextOffset = next.offset();
                            if (nextOffset !== undefined) {
                                var scrTop = that.sc.scrollTop(),
                                        selTop = nextOffset.top - that.sc.offset().top;
                                if (selTop + that.sc.suggestionHeight - that.sc.maxHeight > 0)
                                    that.sc.scrollTop(selTop + that.sc.suggestionHeight + scrTop - that.sc.maxHeight);
                                else if (selTop < 0)
                                    that.sc.scrollTop(selTop + scrTop);
                            }
                        }
                }
            }
            $(window).on('resize.autocomplete', that.updateSC);

            that.sc.appendTo('body');

            that.sc.on('mouseleave', '.autocomplete-suggestion', function () {
                $('.autocomplete-suggestion.selected').removeClass('selected');
            });

            that.sc.on('mouseenter', '.autocomplete-suggestion', function () {
                $('.autocomplete-suggestion.selected').removeClass('selected');
                $(this).addClass('selected');
            });

            that.sc.on('mousedown', '.autocomplete-suggestion', function (e) {
                var item = $(this), v = item.data('val');
                if (v || item.hasClass('autocomplete-suggestion')) { // else outside click
                    that.val(v);
                    o.onSelect(e, v, item, that);
                    that.sc.hide();
                }
                return false;
            });

            that.on('blur.autocomplete', function () {
                try {
                    over_sb = $('.autocomplete-suggestions:hover').length;
                } catch (e) {
                    over_sb = 0;
                } // IE7 fix :hover
                if (!over_sb) {
                    that.last_val = that.val();
                    that.sc.hide();
                    setTimeout(function () {
                        that.sc.hide();
                    }, 350); // hide suggestions on fast input
                } else if (!that.is(':focus'))
                    setTimeout(function () {
                        that.focus();
                    }, 20);
            });

            if (!o.minChars)
                that.on('focus.autocomplete', function () {
                    that.last_val = '\n';
                    that.trigger('keyup.autocomplete');
                });

            function suggest(data) {
                var val = that.val();
                that.cache[val] = data;
                if (data.length && val.length >= o.minChars) {
                    var s = '';
                    for (var i = 0; i < data.length; i++)
                        s += o.renderItem(data[i], val);
                    that.sc.html(s);
                    that.updateSC(0);
                } else
                    that.sc.hide();
            }

            that.on('keydown.autocomplete', function (e) {
                // down (40), up (38)
                if ((e.which == 40 || e.which == 38) && that.sc.html()) {
                    var next, sel = $('.autocomplete-suggestion.selected', that.sc);
                    if (!sel.length) {
                        next = (e.which == 40) ? $('.autocomplete-suggestion', that.sc).first() : $('.autocomplete-suggestion', that.sc).last();
                        that.val(next.addClass('selected').data('val'));
                    } else {
                        next = (e.which == 40) ? sel.next('.autocomplete-suggestion') : sel.prev('.autocomplete-suggestion');
                        if (next.length) {
                            sel.removeClass('selected');
                            that.val(next.addClass('selected').data('val'));
                        } else {
                            sel.removeClass('selected');
                            that.val(that.last_val);
                            next = 0;
                        }
                    }
                    that.updateSC(0, next);
                    return false;
                }
                // esc
                else if (e.which == 27)
                    that.val(that.last_val).sc.hide();
                // enter or tab
                else if (e.which == 13 || e.which == 9) {
                    var sel = $('.autocomplete-suggestion.selected', that.sc);
                    // if (e.which == 13) {
                    e.stopPropagation();
                    //}
                    if (sel.length && that.sc.is(':visible')) {
                        o.onSelect(e, sel.data('val'), sel);
                        setTimeout(function () {
                            that.sc.hide();
                        }, 20);
                    }
                }
            });

            that.on('keyup.autocomplete', function (e) {
                if (!~$.inArray(e.which, [13, 27, 35, 36, 37, 38, 39, 40])) {
                    var val = that.val();
                    if (val.length >= o.minChars) {
                        if (val != that.last_val) {
                            that.last_val = val;
                            clearTimeout(that.timer);
                            if (o.cache) {
                                if (val in that.cache) {
                                    suggest(that.cache[val]);
                                    return;
                                }
                                // no requests if previous suggestions were empty
                                for (var i = 1; i < val.length - o.minChars; i++) {
                                    var part = val.slice(0, val.length - i);
                                    if (part in that.cache && !that.cache[part].length) {
                                        suggest([]);
                                        return;
                                    }
                                }
                            }
                            that.timer = setTimeout(function () {
                                o.source(val, suggest)
                            }, o.delay);
                        }
                    } else {
                        that.last_val = val;
                        that.sc.hide();
                    }
                }
            });
        });
    }

    $.fn.autoComplete.defaults = {
        source: 0,
        minChars: 1,
        delay: 200,
        cache: 1,
        menuClass: '',
        renderItem: function (item, search) {
            var code = item.code;
            // escape special characters
            search = search.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
            var re = new RegExp("(" + search.split(' ').join('|') + ")", "gi");
            return '<div class="autocomplete-suggestion" data-val="' + code + '">' + code.replace(re, "<b>$1</b>") + '</div>';
        },
        onSelect: function (e, term, item) {}
    };
}(jQuery));
